﻿using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.WebControls;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ldapcp
{
    public partial class ClaimsTablePage : LayoutsPageBase
    {
        SPTrustedLoginProvider CurrentTrustedLoginProvider;
        LDAPCPConfig PersistedObject;
        List<KeyValuePair<int, AttributeHelper>> ClaimsMapping;
        bool AllowPersistedObjectUpdate = true;
        public bool ShowNewItemForm = false;
        public bool HideAllContent = false;
        public string TrustName = String.Empty;

        string TextErrorNoTrustAssociation = "LDAPCP is currently not associated with any TrustedLoginProvider. It is mandatory because it cannot create permission for a trust if it is not associated to it.<br/>Visit <a href=\"http://ldapcp.codeplex.com/\" target=\"_blank\">http://ldapcp.codeplex.com/</a> to see how to associate it.<br/>Settings on this page will not be available as long as LDAPCP will not associated to a trut.";
        string TextErrorFieldsMissing = "Some mandatory fields are missing.";
        string TextErrorPersistedObjectStale = "Modification is cancelled because persisted object was modified since last load of the page. Please refresh the page and try again.";
        string TextErrorNoIdentityClaimType = "The TrustedLoginProvider {0} is set with identity claim type \"{1}\" but it is not in the claims list below. LDAPCP will not work until you add this claim type in this list.";
        string TextErrorDuplicateClaimType = "This claim type already exists in the list, you cannot create duplicates.";

        string HtmlCellClaimType = "<span name=\"span_claimtype_{1}\" id=\"span_claimtype_{1}\">{0}</span><input name=\"input_claimtype_{1}\" id=\"input_claimtype_{1}\" style=\"display: none; width: 90%;\" value=\"{0}\"></input>";
        string HtmlCellLAttrName = "<span name=\"span_attrname_{1}\" id=\"span_attrname_{1}\">{0}</span><input name=\"input_attrname_{1}\" id=\"input_attrname_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellLAttrClass = "<span name=\"span_attrclass_{1}\" id=\"span_attrclass_{1}\">{0}</span><input name=\"input_attrclass_{1}\" id=\"input_attrclass_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellLDAPAttrToDisplay = "<span name=\"span_LDAPAttrToDisplay_{1}\" id=\"span_LDAPAttrToDisplay_{1}\">{0}</span><input name=\"input_LDAPAttrToDisplay_{1}\" id=\"input_LDAPAttrToDisplay_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellLAddLDAPFilter = "<span name=\"span_AddLDAPFilter_{1}\" id=\"span_AddLDAPFilter_{1}\">{0}</span><input name=\"input_AddLDAPFilter_{1}\" id=\"input_AddLDAPFilter_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellKeywordToValidateInputWithoutLookup = "<span name=\"span_KeywordToValidateInputWithoutLookup_{1}\" id=\"span_KeywordToValidateInputWithoutLookup_{1}\">{0}</span><input name=\"input_KeywordToValidateInputWithoutLookup_{1}\" id=\"input_KeywordToValidateInputWithoutLookup_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellPrefixToAddToValueReturned = "<span name=\"span_PrefixToAddToValueReturned_{1}\" id=\"span_PrefixToAddToValueReturned_{1}\">{0}</span><input name=\"input_PrefixToAddToValueReturned_{1}\" id=\"input_PrefixToAddToValueReturned_{1}\" style=\"display:none;\" value=\"{0}\"></input>";
        string HtmlCellClaimEntityType = "<span name=\"span_ClaimEntityType_{1}\" id=\"span_ClaimEntityType_{1}\">{0}</span><select name=\"list_ClaimEntityType_{1}\" id=\"list_ClaimEntityType_{1}\" style=\"display:none;\">{2}</select>";

        string HtmlEditLink = "<a name=\"editLink_{0}\" id=\"editLink_{0}\" href=\"javascript:Ldapcp.ClaimsTablePage.EditItem('{0}')\">Edit</a>";
        string HtmlCancelEditLink = "<a name=\"cancelLink_{0}\" id=\"cancelLink_{0}\" href=\"javascript:Ldapcp.ClaimsTablePage.CancelEditItem('{0}')\" style=\"display:none;\">Cancel</a>";

        protected void Page_Load(object sender, EventArgs e)
        {
            // Get trust currently associated with LDAPCP, if any
            CurrentTrustedLoginProvider = LDAPCP.GetSPTrustAssociatedWithCP(LDAPCP._ProviderInternalName);
            if (null == CurrentTrustedLoginProvider)
            {
                // Claim provider is currently not associated with any trust.
                // Display a message in the page and disable controls
                this.LabelErrorMessage.Text = TextErrorNoTrustAssociation;
                this.HideAllContent = true;
                this.BtnCreateNewItem.Visible = false;
                return;
            }

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                // Get SPPersisted Object and create it if it doesn't exist
                PersistedObject = LDAPCPConfig.GetFromConfigDB();
                if (PersistedObject == null)
                {
                    this.Web.AllowUnsafeUpdates = true;
                    PersistedObject = LDAPCPConfig.CreatePersistedObject();
                    this.Web.AllowUnsafeUpdates = false;
                }
            });

            if (ViewState["PersistedObjectVersion"] == null)
                ViewState.Add("PersistedObjectVersion", PersistedObject.Version);
            if ((long)ViewState["PersistedObjectVersion"] != PersistedObject.Version)
            {
                // PersistedObject changed since last time. Should not allow any update
                this.LabelErrorMessage.Text = TextErrorPersistedObjectStale;
                this.AllowPersistedObjectUpdate = false;
                return;
            }
            TrustName = CurrentTrustedLoginProvider.Name;

            if (!this.IsPostBack)
            {
                RdbNewItemClassicClaimType.Attributes.Add("onClick",
                    "$('#divNewItemControls').show('slow');" +
                    "$('#rowClaimType').show('slow');" +
                    "$('#emPermissionMetadata').hide('slow');");
                RdbNewItemPermissionMetadata.Attributes.Add("onClick",
                    "$('#divNewItemControls').show('slow');" +
                    "$('#rowClaimType').hide('slow');" +
                    "$('#emPermissionMetadata').show('slow');");
                RdbNewItemLinkdedToIdClaim.Attributes.Add("onClick",
                    "$('#divNewItemControls').show('slow');" +
                    "$('#rowClaimType').hide('slow');" +
                    "$('#emPermissionMetadata').hide('slow');");

                DdlPermissionMetadata.Items.Add(String.Empty);
                Type EntityDataKeysInfo = typeof(PeopleEditorEntityDataKeys);
                object[] fields = EntityDataKeysInfo.GetFields();
                foreach (object field in fields)
                {
                    DdlPermissionMetadata.Items.Add(((System.Reflection.FieldInfo)field).Name);
                }
            }

            BuildAttributesListTable(this.IsPostBack);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pendingUpdate">true if there is a post back, which means an update is being made</param>
        private void BuildAttributesListTable(bool pendingUpdate)
        {
            // Copy claims list in a key value pair so that each item has a unique ID that can be used later for update/delete operations
            ClaimsMapping = new List<KeyValuePair<int, AttributeHelper>>();
            int i = 0;
            foreach (AttributeHelper attr in this.PersistedObject.AttributesListProp)
            {
                ClaimsMapping.Add(new KeyValuePair<int, AttributeHelper>(i++, attr));
            }

            bool identityClaimPresent = false;

            TblClaimsMapping.Rows.Clear();
            TableRow tr = new TableRow();
            TableHeaderCell th;
            th = GetTableHeaderCell("Action");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("Claim type");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("LDAP Attribute");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("LDAP class");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("Attribute to display");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("<a href='http://msdn.microsoft.com/en-us/library/office/microsoft.sharepoint.administration.claims.spclaimentitytypes_members(v=office.15).aspx' target='_blank'>Claim entity type</a>");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("Additional LDAP filter");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("Prefix to bypass lookup");
            tr.Cells.Add(th);
            th = GetTableHeaderCell("Prefix to add to value returned");
            tr.Cells.Add(th);
            this.TblClaimsMapping.Rows.Add(tr);

            foreach (var attr in this.ClaimsMapping)
            {
                tr = new TableRow();
                bool allowEditItem = String.IsNullOrEmpty(attr.Value.ClaimType) ? false : true;

                // Actions links
                // LinkButton must always be created otherwise event receiver will not fire on postback 
                LinkButton LnkDeleteItem = new LinkButton();
                LnkDeleteItem.ID = "DeleteItemLink_" + attr.Key;
                LnkDeleteItem.Command += LnkDeleteItem_Command;
                LnkDeleteItem.CommandArgument = attr.Key.ToString();
                LnkDeleteItem.Text = "Delete";
                LnkDeleteItem.OnClientClick = "javascript:return confirm('This will delete this item. Do you want to continue?');";
                if (pendingUpdate) LnkDeleteItem.Visible = false;

                LinkButton LnkUpdateItem = new LinkButton();
                LnkUpdateItem.ID = "UpdateItemLink_" + attr.Key;
                LnkUpdateItem.Command += LnkUpdateItem_Command;
                LnkUpdateItem.CommandArgument = attr.Key.ToString();
                LnkUpdateItem.Text = "Save";
                LnkUpdateItem.Style.Add("display", "none");
                if (pendingUpdate) LnkUpdateItem.Visible = false;

                TableCell tc = new TableCell();
                if (allowEditItem) tc.Controls.Add(new LiteralControl(String.Format(HtmlEditLink, attr.Key) + "&nbsp;&nbsp;"));
                tc.Controls.Add(LnkDeleteItem);
                tc.Controls.Add(LnkUpdateItem);
                tc.Controls.Add(new LiteralControl("&nbsp;&nbsp;" + String.Format(HtmlCancelEditLink, attr.Key)));

                tr.Cells.Add(tc);

                // This is just to avoid building the table if we know that there is a pending update, which means it will be rebuilt again after update is complete
                if (!pendingUpdate)
                {
                    string html;
                    TableCell c = null;
                    // Check if claim type is set, and if it exists in the current trust
                    if (!String.IsNullOrEmpty(attr.Value.ClaimType))
                    {
                        html = String.Format(HtmlCellClaimType, attr.Value.ClaimType, attr.Key);
                        c = GetTableCell(html);
                        allowEditItem = true;
                        if (String.Equals(CurrentTrustedLoginProvider.IdentityClaimTypeInformation.MappedClaimType, attr.Value.ClaimType, StringComparison.InvariantCultureIgnoreCase) && !attr.Value.CreateAsIdentityClaim)
                        {
                            tr.CssClass = "ldapcp-rowidentityclaim";
                            identityClaimPresent = true;
                        }
                        else if (CurrentTrustedLoginProvider.ClaimTypeInformation.FirstOrDefault(x => String.Equals(x.MappedClaimType, attr.Value.ClaimType, StringComparison.InvariantCultureIgnoreCase)) == null)
                        {
                            tr.CssClass = "ldapcp-rowClaimTypeNotUsedInTrust";
                        }
                        else
                        {
                            tr.CssClass = "ldapcp-rowClaimTypeOk";
                        }
                    }
                    else
                    {
                        c = GetTableCell(attr.Value.CreateAsIdentityClaim ? "linked to identity claim" : "Used as metadata for the permission created");
                    }
                    tr.Cells.Add(c);

                    html = String.Format(HtmlCellLAttrName, attr.Value.LDAPAttribute, attr.Key);
                    tr.Cells.Add(GetTableCell(html));

                    html = String.Format(HtmlCellLAttrClass, attr.Value.LDAPObjectClassProp, attr.Key);
                    tr.Cells.Add(GetTableCell(html));

                    html = String.Format(HtmlCellLDAPAttrToDisplay, attr.Value.LDAPAttributeToDisplayProp, attr.Key);
                    tr.Cells.Add(GetTableCell(html));

                    html = BuildClaimEntityTypeDDL(attr);
                    tr.Cells.Add(GetTableCell(html));

                    html = String.Format(HtmlCellLAddLDAPFilter, attr.Value.AdditionalLDAPFilterProp, attr.Key);
                    tr.Cells.Add(GetTableCell(html));

                    html = String.Format(HtmlCellKeywordToValidateInputWithoutLookup, attr.Value.PrefixToBypassLookup, attr.Key);
                    tr.Cells.Add(GetTableCell(html));

                    html = String.Format(HtmlCellPrefixToAddToValueReturned, attr.Value.PrefixToAddToValueReturnedProp, attr.Key);
                    tr.Cells.Add(GetTableCell(html));
                }
                TblClaimsMapping.Rows.Add(tr);
            }

            if (!identityClaimPresent && !pendingUpdate)
            {
                LabelErrorMessage.Text = String.Format(this.TextErrorNoIdentityClaimType, CurrentTrustedLoginProvider.DisplayName, CurrentTrustedLoginProvider.IdentityClaimTypeInformation.MappedClaimType);
            }
        }

        private string BuildClaimEntityTypeDDL(KeyValuePair<int, AttributeHelper> attr)
        {
            string option = "<option value=\"{0}\" {1}>{2}</option>";
            string selected = String.Empty;
            StringBuilder options = new StringBuilder();

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.DistributionList ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.DistributionList, selected, "DistributionList"));

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.FormsRole ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.FormsRole, selected, SPClaimEntityTypes.FormsRole));

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.SecurityGroup ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.SecurityGroup, selected, "SecurityGroup"));

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.System ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.System, selected, SPClaimEntityTypes.System));

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.Trusted ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.Trusted, selected, SPClaimEntityTypes.Trusted));

            selected = attr.Value.ClaimEntityType == SPClaimEntityTypes.User ? "selected" : "";
            options.Append(String.Format(option, SPClaimEntityTypes.User, selected, SPClaimEntityTypes.User));

            return String.Format(HtmlCellClaimEntityType, attr.Value.ClaimEntityType, attr.Key, options.ToString());
        }

        private TableHeaderCell GetTableHeaderCell(string Value)
        {
            TableHeaderCell tc = new TableHeaderCell();
            tc.Text = Value;
            return tc;
        }
        private TableCell GetTableCell(string Value)
        {
            TableCell tc = new TableCell();
            tc.Text = Value;
            return tc;
        }

        protected void UpdatePersistedObject()
        {
            if (null == PersistedObject)
            {
                LDAPCP.LogToULS(
                    String.Format("PersistedObject {0} should not be null.", Constants.LDAPCPCONFIG_NAME),
                    TraceSeverity.Unexpected,
                    EventSeverity.Error,
                    LdapcpLoggingService.Categories.Configuration);
                return;
            }

            if (null == CurrentTrustedLoginProvider)
            {
                LDAPCP.LogToULS(
                    "Trust associated with LDAPCP could not be found.",
                    TraceSeverity.Unexpected,
                    EventSeverity.Error,
                    LdapcpLoggingService.Categories.Configuration);
                return;
            }

            // Update object in database
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                this.Web.AllowUnsafeUpdates = true;
                PersistedObject.Update();
                this.Web.AllowUnsafeUpdates = false;

                LDAPCP.LogToULS(
                    String.Format("Attributes list of LDAPCP has been successfully updated in PersistedObject {0}.", Constants.LDAPCPCONFIG_NAME),
                    TraceSeverity.Medium,
                    EventSeverity.Information,
                    LdapcpLoggingService.Categories.Configuration);
            });
            ViewState["PersistedObjectVersion"] = PersistedObject.Version;
        }

        void LnkDeleteItem_Command(object sender, CommandEventArgs e)
        {
            if (!this.AllowPersistedObjectUpdate) return;

            string itemId = e.CommandArgument.ToString();
            AttributeHelper attr = ClaimsMapping.Find(x => x.Key == Convert.ToInt32(itemId)).Value;
            PersistedObject.AttributesListProp.Remove(attr);
            this.UpdatePersistedObject();
            this.BuildAttributesListTable(false);
        }

        void LnkUpdateItem_Command(object sender, CommandEventArgs e)
        {
            if (!this.AllowPersistedObjectUpdate) return;

            string itemId = e.CommandArgument.ToString();

            NameValueCollection formData = Request.Form;
            if (String.IsNullOrEmpty(formData["input_claimtype_" + itemId]) || String.IsNullOrEmpty(formData["input_attrname_" + itemId]) || String.IsNullOrEmpty(formData["input_attrclass_" + itemId]))
            {
                this.LabelErrorMessage.Text = TextErrorFieldsMissing;
                return;
            }

            AttributeHelper attr = ClaimsMapping.Find(x => x.Key == Convert.ToInt32(itemId)).Value;
            attr.ClaimType = formData["input_claimtype_" + itemId];
            attr.LDAPAttribute = formData["input_attrname_" + itemId];
            attr.LDAPObjectClassProp = formData["input_attrclass_" + itemId];
            attr.LDAPAttributeToDisplayProp = formData["input_LDAPAttrToDisplay_" + itemId];
            attr.ClaimEntityType = formData["list_ClaimEntityType_" + itemId];
            attr.AdditionalLDAPFilterProp = formData["input_AddLDAPFilter_" + itemId];
            attr.PrefixToBypassLookup = formData["input_KeywordToValidateInputWithoutLookup_" + itemId];
            attr.PrefixToAddToValueReturnedProp = formData["input_PrefixToAddToValueReturned_" + itemId];

            this.UpdatePersistedObject();
            this.BuildAttributesListTable(false);
        }

        protected void BtnReset_Click(object sender, EventArgs e)
        {
            LDAPCPConfig.ResetClaimsList();
            Response.Redirect(Request.Url.ToString());
        }

        protected void BtnCreateNewItem_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(TxtNewAttrName.Text) || String.IsNullOrEmpty(TxtNewObjectClass.Text))
            {
                this.LabelErrorMessage.Text = TextErrorFieldsMissing;
                ShowNewItemForm = true;
                BuildAttributesListTable(false);
                return;
            }

            AttributeHelper attr = new AttributeHelper();
            if (RdbNewItemClassicClaimType.Checked)
            {
                if (String.IsNullOrEmpty(TxtNewClaimType.Text))
                {
                    this.LabelErrorMessage.Text = TextErrorFieldsMissing;
                    ShowNewItemForm = true;
                    BuildAttributesListTable(false);
                    return;
                }

                attr.ClaimType = TxtNewClaimType.Text;
                if (PersistedObject.AttributesListProp.Find(x => String.Equals(attr.ClaimType, x.ClaimType, StringComparison.InvariantCultureIgnoreCase)) != null)
                {
                    this.LabelErrorMessage.Text = TextErrorDuplicateClaimType;
                    ShowNewItemForm = true;
                    BuildAttributesListTable(false);
                    return;
                }
            }
            else if (RdbNewItemPermissionMetadata.Checked)
            {
                if (String.IsNullOrEmpty(DdlPermissionMetadata.SelectedValue))
                {
                    this.LabelErrorMessage.Text = TextErrorFieldsMissing;
                    ShowNewItemForm = true;
                    BuildAttributesListTable(false);
                    return;
                }
            }
            else attr.CreateAsIdentityClaim = true;

            attr.LDAPAttribute = TxtNewAttrName.Text;
            attr.LDAPObjectClassProp = TxtNewObjectClass.Text;
            attr.ClaimEntityType = SPClaimEntityTypes.User;
            attr.EntityDataKey = DdlPermissionMetadata.SelectedValue;

            PersistedObject.AttributesListProp.Add(attr);
            UpdatePersistedObject();
            BuildAttributesListTable(false);
        }
    }
}
